#!/usr/bin/env python3
# -*- encoding: utf-8 -*-


import sys
sys.path.append('..')
from common.functions import *
from common.gradient import numerical_gradient


class TwoLayerNet:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        """
        构造函数
        :param input_size: 输入层神经元数目
        :param hidden_size: 中间层神经元数目
        :param output_size: 输出层神经元数目
        :param weight_init_std: 初始化时权重的标准差
        """
        self.params = {
            'W1': weight_init_std * np.random.randn(input_size, hidden_size),
            'b1': np.zeros(hidden_size),
            'W2': weight_init_std * np.random.randn(hidden_size, output_size),
            'b2': np.zeros(output_size)
        }

    # 预测函数
    def predict(self, x):
        a1 = np.dot(x, self.params['W1']) + self.params['b1']
        z1 = sigmoid(a1)

        a2 = np.dot(z1, self.params['W2']) + self.params['b2']
        y = softmax(a2)

        return y

    # 损失函数
    def loss(self, x, t):
        y = self.predict(x)

        return cross_entropy_error(y, t)

    # 预测准确度
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        t = np.argmax(t, axis=1)

        res = np.sum(y == t) / float(y.shape[0])
        return res

    # 使用数值微分法计算梯度
    def numerical_gradient(self, x, t):
        """
        使用数值微分法计算梯度
        :param x: 输入数据
        :param t: 监督数据
        :return: 梯度
        """
        loss_W = lambda W: self.loss(x, t)
        grad = {}
        for key in ('W1', 'b1', 'W2', 'b2'):
            grad[key] = numerical_gradient(loss_W, self.params[key])
        return grad
